package com.fin.zw.aiqas.service;

import com.fin.zw.aiqas.entity.*;
import com.fin.zw.aiqas.enums.*;
import com.fin.zw.aiqas.repository.*;
import com.fin.zw.aiqas.util.AudioUtile;
import com.fin.zw.aiqas.util.BadRequestException;
import com.fin.zw.aiqas.util.FileUtil;
import com.fin.zw.aiqas.util.VideoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.ListUtils;
import org.thymeleaf.util.StringUtils;

import java.io.File;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;

//import com.fin.zw.aiqas.util.SCPUtilsTwo;

@Service("executeTaskInfoService")
public class ExecuteTaskInfoService {

    private final static Logger log = LoggerFactory.getLogger(ExecuteTaskInfoService.class);

    @Autowired
    private TaskInfoRepository taskInfoRepository;

    @Autowired
    private InspectionRepository inspectionRepository;

    @Autowired
    private InspectionDetailRepository inspectionDetailRepository;

    @Autowired
    private UserInfoRepository userInfoRepository;

    @Autowired
    private DepartmentRepository departmentRepository;

    @Autowired
    private ExecuteDetailFileService executeDetailFileService;

    private Map<String, Object> result = new ConcurrentHashMap<>();

    @Value("${scp.port}")
    private int scp_port;

    @Value("${scp.basePath}")
    private String scp_basePath;

    @Value("${scp.host}")
    private String scp_host;

    @Value("${scp.username}")
    private String scp_username;

    @Value("${scp.password}")
    private String scp_password;

    @Value("${scp.localFile}")
    private String scp_localFile;

    //@Async
    public void sendTaskInfo(Long taskId) {
        log.info("根据任务id:" + taskId + "获取任务id");
        TaskInfo taskInfo = getTaskInfoByTaskId(taskId);
        log.info("任务信息：" + taskInfo);
        InspectionInfo info = saveInspection(taskInfo);
        log.info("设置执行结果信息：" + info);
        List<File> urls = getFilePathByDirectory(taskInfo.getTaskFilePath());
        log.info("根据任务目录获取对应的文件");
        taskInfo.setStartTime(new Date());
        int size = 0;//计算出最终需要执行的文件数量
        Map<String, Integer> map = new HashMap<>();
        List<File> results = new ArrayList<>();
//        setTaskInfoByFiles(taskInfo, urls, results, map, size);
        if (ListUtils.isEmpty(urls)) {
            taskInfo.setRunStatus(TaskStatus.FINISH);
            taskInfo.setEndTime(new Date());
        } else {
            List<File> files = filtrateTaskInfoFile(urls, taskInfo, map);
            results.addAll(files);
            //计算抽检数量
            Double number = results.size() * taskInfo.getCheckProp().doubleValue() * 0.01;
            size = number.intValue();
            if (size < 1) {
                taskInfo.setEndTime(new Date());
                taskInfo.setRunStatus(TaskStatus.FINISH);
            }
        }
        log.info("根据文件情况设置任务状态：" + taskInfo);
        if (!ListUtils.isEmpty(urls) && results.size() > 0) {
            List<File> files = createRandomList(results, size);
            //保存质检结果
            info.setVoiceNum(results.size());
            info = inspectionRepository.save(info);
            List<InspectionDetail> details = saveInspectionDetail(taskInfo, files, info, map);
            log.info("质检明细保存信息：" + details);
            details = inspectionDetailRepository.saveAll(details);
            //任务上可以设置并行数，下面执行可以从任务上获取并行数
            int count =  taskInfo.getThreadNum();
            List<FutureTask<InspectionDetail>> futures = executeThread(details, taskInfo.getId(), count);
            updateInspection(futures,info,taskInfo);
        } else {
            inspectionRepository.save(info);
        }
        taskInfoRepository.save(taskInfo);
    }


    /**
     * 处理线程
     * @param details
     * @param taskId
     */
    public List<FutureTask<InspectionDetail>> executeThread(List<InspectionDetail> details, Long taskId, Integer parallelCount) {
        List<FutureTask<InspectionDetail>> futures = new CopyOnWriteArrayList<>();
        ExecutorService executorService = Executors.newFixedThreadPool(parallelCount);
        for (InspectionDetail detail : details) {
            FutureTask<InspectionDetail> future = new FutureTask<>(new Callable<InspectionDetail>() {
                @Override
                public InspectionDetail call() throws Exception {
                    return executeDetailFileService.executeTaskInfo(detail,taskId);
                }
            }
            );
            futures.add(future);
            executorService.submit(future);
        }

        executorService.shutdown();
        return futures;
    }


    /**
     * 更新任务的状态
     * @param futures
     * @param info
     * @param taskInfo
     */
    public void updateInspection(List<FutureTask<InspectionDetail>> futures,InspectionInfo info,TaskInfo taskInfo){
        if(!ListUtils.isEmpty(futures)){
            Integer successNumber = 0;//命中规则的文件数量
            for(FutureTask<InspectionDetail> future : futures){
                try {
                    InspectionDetail inspectionDetail = future.get();
                    if(inspectionDetail.getHitStatus().equals(HitStatus.HIT)){
                        successNumber ++;
                    }
                } catch (Exception e) {
                    log.error("获取线程返回值出错.....");
                }
            }
            info.setHitNum(successNumber);
            BigDecimal hitNum = new BigDecimal(successNumber);
            BigDecimal voiceNumber = new BigDecimal(info.getVoiceNum());
            if (info.getVoiceNum() == 0) {
                info.setHitScale(new BigDecimal(0));
            }else{
                BigDecimal hitScale = hitNum.divide(voiceNumber, 4, BigDecimal.ROUND_HALF_UP);
                info.setHitScale(hitScale);
            }
            info.setTaskStatus(InspTask.FINISH);
            inspectionRepository.save(info);
            taskInfo.setRunStatus(TaskStatus.FINISH);
            taskInfo.setEndTime(new Date());
        }
    }


    /**
     * 根据任务id获取任务信息
     *
     * @param taskId
     * @return
     */
    public TaskInfo getTaskInfoByTaskId(Long taskId) {
        Optional<TaskInfo> optional = taskInfoRepository.findById(taskId);
        if (!optional.isPresent()) {
            throw new BadRequestException("", "推送的任务信息不存在！+");
        }
        TaskInfo taskInfo = optional.get();
        return taskInfo;
    }

    /**
     * 质检结果处理
     *
     * @param taskInfo
     * @return
     */
    public InspectionInfo saveInspection(TaskInfo taskInfo) {
        InspectionInfo inspectionInfo = new InspectionInfo();
        inspectionInfo.setTaskInfo(taskInfo);
        inspectionInfo.setRunDate(new Date());
        inspectionInfo.setVoiceNum(0);
        inspectionInfo.setComplateNum(0);
        inspectionInfo.setTaskStatus(InspTask.EXCUTING);
        inspectionInfo.setHitNum(0);
        inspectionInfo.setHitScale(new BigDecimal(0));
        inspectionInfo.setCreateTime(new Date());
        inspectionInfo.setInspectionBatch(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        return inspectionInfo;
    }

    /**
     * 根据任务文件路径获取对应的文件
     *
     * @param directory
     * @return
     */
    public List<File> getFilePathByDirectory(String directory) {
        List<File> results = new ArrayList<>();
        if (StringUtils.isEmpty(directory)) {
            LocalDate localDate = LocalDate.now();
            String nowDate = localDate.toString().replace("-", "");
            String taskFilePath = scp_localFile + nowDate;
            //SCPUtilsTwo.SCPInit(scp_port, scp_basePath, scp_host, scp_username, scp_password, taskFilePath);
            //log.info("文件目录是：" + taskFilePath);
            //results = FileUtil.getAllFiles(taskFilePath);
            log.info("文件目录不能为空，暂不支持勇乾的方式");
        } else {
            log.info("文件目录是：" + directory);
            results = FileUtil.getAllFiles(directory);
        }
        return results;
    }

    /**
     * 针对文件条件进行条件过滤
     *
     * @param files
     * @param taskInfo
     * @param map
     * @return
     */
    public List<File> filtrateTaskInfoFile(List<File> files, TaskInfo taskInfo, Map<String, Integer> map) {
        List<File> results = new ArrayList<>();
        for (File file : files) {
            String filePath = file.getPath();
            try {
                Map<String, Object> audioInfo = VideoUtil.getAudioInfo(file);
                Long duration = ((Double)audioInfo.get("duration")).longValue();
                BigDecimal voice = new BigDecimal(duration);
                Integer fmt =  ((Short)audioInfo.get("fmt")).intValue();
                log.info(String.format("文件%s得编码格式为：%d, 时长为：%s", filePath, fmt, voice.intValue()));
                if(fmt == 49 && file.getName().toLowerCase().endsWith("wav")){
                    String newPath = AudioUtile.ChangeWavCodec(file);
                    if(newPath != file.getPath()){
                        log.info(String.format("文件%s成功转码为pcm编码得wav格式,%s", filePath,newPath));
                        filePath = newPath;
                    }
                }


                int cmpStart = voice.compareTo(taskInfo.getNum());
                int cmpEnd = voice.compareTo(taskInfo.getEndNum()==null?new BigDecimal(Long.MAX_VALUE):taskInfo.getEndNum());

                if(taskInfo.getDurationLogic().equals(LogicalCalculus.LESS) && cmpStart != -1)
                    continue;
                if (taskInfo.getDurationLogic().equals(LogicalCalculus.LARGER) && cmpStart != 1)
                    continue;
                if (taskInfo.getDurationLogic().equals(LogicalCalculus.CUSTOM) && (cmpEnd!=-1 || cmpStart!=1))
                    continue;
                log.info("收集到文件%s", filePath);
                if(filePath != file.getPath())
                    results.add(new File(filePath));
                else
                    results.add(file);

                map.put(filePath, duration.intValue());

            } catch (Exception e) {
                log.error("方法filtrateTaskInfoFile获取文件时长并条件过滤出错:" + e.getMessage());
                e.printStackTrace();
            }
        }
        return results;
    }


    /**
     * 从List中随机出n个对象
     *
     * @param list
     * @param num
     * @return
     */
    private static List<File> createRandomList(List<File> list, int num) {
        List<Integer> number = new ArrayList<>();
        List<File> listNew = new ArrayList();
        if (list.size() <= num) return list;
        while (number.size() < num) {
            int random = (int) (Math.random() * list.size());
            if (!number.contains(random)) {
                number.add(random);
                log.info(random + "随机抽取的对象：" + list.get(random));
                listNew.add(list.get(random));
            }
        }
        return listNew;
    }

    /**
     * 处理质检明细
     *
     * @param taskInfo
     * @param urls
     * @return
     */
    public List<InspectionDetail> saveInspectionDetail(TaskInfo taskInfo, List<File> urls, InspectionInfo inspectionInfo, Map<String, Integer> map) {
        List<InspectionDetail> list = new ArrayList<>();
        for (File file : urls) {
            InspectionDetail inspectionDetail = new InspectionDetail();
            inspectionDetail.setInspectionInfo(inspectionInfo);
            inspectionDetail.setTaskInfo(taskInfo);
            inspectionDetail.setVoiceUrl(file.getPath());
            inspectionDetail.setHitStatus(HitStatus.MISS);
            //设置客服编号和部门
            setDetailProperty(file.getPath(), inspectionDetail);
            //setUserNameDepartment(file, inspectionDetail);
            //设置质检状态
            inspectionDetail.setQualityStatus(QualityStatus.READY);//就绪状态
            inspectionDetail.setCreateTime(new Date());
            if (map.containsKey(file.getPath())) {
                inspectionDetail.setVoiceLength(map.get(file.getPath()));
            } else {
                inspectionDetail.setVoiceLength(0);
            }
            list.add(inspectionDetail);
        }
        return list;
    }


    /**
     * 根据文件路径解析客户编号和机构
     *
     * @param url
     * @param info
     */
    public void setDetailProperty(String url, InspectionDetail info) {

        //  部门编号/座席编码_分机/文件名
        if(url.contains("/") || url.contains("\\")){
            String[] pathSplit = url.contains("/") ? url.split("/") : url.split("\\\\") ;
            int pLen = pathSplit.length;
            String fname = pLen > 0 ? pathSplit[pLen-1] : "unKnow";        //文件名
            String scode_agent = pLen > 1 ? pathSplit[pLen-2] : "unKnow_unKnow";        //到机构目录路径
            String departCode = pLen > 2 ? pathSplit[pLen-3] : "unKnow";        //部门编号
            if(scode_agent.contains("_")) {
                String[] scodeAgent = scode_agent.split("_");
                info.setServiceCode(scodeAgent[0]);
                info.setServiceExt(scodeAgent[1]);
            }else{
                info.setServiceCode(scode_agent);
                info.setServiceExt(scode_agent);
            }
            info.setFileName(fname);
            Optional<Department> optional = departmentRepository.findOne(QDepartment.department.code.eq(departCode));
            if (optional.isPresent()) {
                info.setDepartment(optional.get());
            }
        }
    }

    public String getFile(String path) {
        File file = new File(path);
        return file.getParent();
    }

    /**
     * 根据分机号获取对应的用户信息
     *
     * @param file
     * @param info
     */
    public void setUserNameDepartment(File file, InspectionDetail info) {
        String name = file.getName();
        String phone = new String();
        String[] mgs = name.split("-");
        if (name.contains("out")) {
            phone = mgs[2];
        } else {
            phone = mgs[1];
        }
        Iterator<UserInfo> iterator = userInfoRepository.findAll(QUserInfo.userInfo.phone.eq(phone)).iterator();
        if (iterator.hasNext()) {
            UserInfo userInfo = iterator.next();
            if (Objects.nonNull(userInfo.getAccount())) {
                info.setServiceCode(userInfo.getAccount().getName());
            }
            if (Objects.nonNull(userInfo.getDepartment())) {
                info.setDepartment(userInfo.getDepartment());
            }
        }
    }

    /**
     * 根据文件数量进行设置任务属性
     *
     * @param taskInfo
     * @param urls
     * @param results
     * @param map
     * @param size
     */
    public void setTaskInfoByFiles(TaskInfo taskInfo, List<File> urls, List<File> results, Map<String, Integer> map, int size) {
        if (ListUtils.isEmpty(urls)) {
            taskInfo.setRunStatus(TaskStatus.FINISH);
            taskInfo.setEndTime(new Date());
        } else {
            List<File> files = filtrateTaskInfoFile(urls, taskInfo, map);
            results.addAll(files);
            //计算抽检数量
            Double number = results.size() * taskInfo.getCheckProp().doubleValue() * 0.01;
            size = number.intValue();
            if (size < 1) {
                taskInfo.setEndTime(new Date());
                taskInfo.setRunStatus(TaskStatus.FINISH);
            }
        }
    }
}